<?php
/**
 * @author Eugenio Fage
 * http://eugeniofage.wordpress.com/
 * http://code.google.com/p/monky/
 * 
 */
class Link extends AppModel {
	var $name = 'Link';
	
	var $actsAs=array('Ban');
	
	var $belongsTo = array(
		'User',	'Blog',	'Category'
	);

	var $hasMany = array(
		'Comment','Favorite','Tag',
		'Trackback',
		'Vote'=>array(
	 		'conditions' => array('Vote.type' => 'links'),
		)
	);
	
	var $pageObj;
    
    
    function setPageobj($url){
    	App::import('vendor','robot');
   		$this->pageObj=new Page($url);
   		$this->Blog->saveBlog($this->pageObj);
    }
    
    function vote($vote){
		$this->id=$vote['Link']['id'];
				
		$vote=$this->Vote->setVoteData($vote);
				
		$this->_checkErrorInVote($vote);
		
		$this->addLinkVote($vote);
				
		$this->recursive=-1;
		$updatedLink=$this->read(array('id','votes','anonymous','negatives','karma'));
		return $updatedLink;
	}
    

	function publish($id){
		$this->id=$id;
		
		if(!$this->field('Link.status')=='queued') return;
		
		$votes_avg = $this->Vote->find('all',array(
			'fields'=>array('avg(Vote.value) as avgvote'),
			'conditions'=>array('Vote.type'=>'links', 'Vote.link_id'=> $id,'Vote.user_id >'=> 0,'Vote.value >'=> 0)
		));

		$this->saveField('status','published');
		
		$this->saveField('votes_avg',$votes_avg[0][0]['avgvote']);


		$this->User->id=$this->field('Link.user_id');
		$karma = min(20, $this->User->field('User.karma') + Configure::read('Monky.Karma.instant_karma_per_published'));
		$this->User->saveField('User.karma',$karma);
			
		// @todo
		// add published log
		// update user karma detail
//		$annotation->append(_('noticia publicada').": +". $globals['instant_karma_per_published'] .", karma: $user->karma\n");
		

		App::import('vendor',array('bitly','tweet'));
		
		$TWITEER=Configure::read('Twitter');
		
		if($TWITEER['user'] && $TWITEER['password']){
			$BITLY=Configure::read('Bitly');
			$bitly = new Bitly($BITLY['username'],$BITLY['apikey']);
						
			$title=$this->field('title');
			$url=Configure::read('Monky.fullweb').Router::url(array('controller'=>'links','action'=>'view','slug'=>Inflector::slug($title),'id'=>$this->id));
			$url=$bitly->shorten($url);
			twitterSetStatus($TWITEER['user'],$TWITEER['password'],$title.'... '.$url);
		}
	
	}
    
    function isContentShare(){    	
    	return !$this->pageObj->noShare;
    }
    
    function isFound($e){
    	return ($this->pageObj->errorMsg)? $this->pageObj->errorMsg : true ;
	}
    
    
    /**
	 * 
	 * chequea que la url no este repetida
	 * @param $linkData
	 */
    function validateDuplicate($linkData){
    	if($id=$this->getDuplicateLinkId(array('Link'=>array('url'=>$linkData['url'])))){
       		$this->id=$id;
       		return sprintf(__('The url has already been sent <a href="%s">Click here to see it</a>',true),Router::url(array('controller'=>'links','action'=>'view','slug'=>Inflector::slug($this->field('title')),'id'=>$id)));
       	}
       	return true;
    }
    
    /**
	 * 
	 * chequea que el usuario no envie spam 
	 * @param $linkData
	 * TODO quitar numeros magicos
	 */
	function validateAutobombo(){
		$DAYS=Configure::read('Monky.Autobombo.days');
		$ratio=0;
		$same_blog_sents=0;
	
		
		if($sents=$this->getTotalSents($DAYS,null)>0){
			$same_blog_sents=$this->getTotalSents($DAYS,$this->data['Blog']['key']);
			$ratio = $same_blog_sents/$sents;	
		}
			
				
		//envia posible spam
		if($sents > Configure::read('Monky.Autobombo.isSpam_sents') && $ratio > Configure::read('Monky.Autobombo.isSpam_ratio')){
			//loguear conducata sospechosa
			CakeLog::write('alerts','Meneame, warn, high ratio, process interrumped, user: '.$this->data['Link']['user_id']);
			return false;
		}

		if($sents > Configure::read('Monky.Autobombo.warning_sents') && $same_blog_sents > Configure::read('Monky.Autobombo.same_blog_sents') && $ratio > Configure::read('Monky.Autobombo.warning_ratio')){
			$this->warning=__('You have send too many links, you could recive negative karma',true);
		}
		
		return true;
	}
	
	function isVotable($link) {
		
		if(Visitor::isBot()) return false;
		if(!Configure::read('Monky.Votes.anon_enable') && $link['User']['id']==0) return false;
		
		$link=$this->prepareFields($link, array('status','created'));
				
		if(in_array($link['Link']['status'],array('abuse','autodiscard'))) return false;
		
		return $this->isLinkInVoteInterval($link);
	}
	
	private function isLinkInVoteInterval($link){
		$time_enabled=Configure::read('Monky.Votes.time_enabled');
		if($time_enabled<=0) return true;
		
		$link=$this->prepareFields($link, array('created'));
		
		$createdinterval=time()-getUnixtime($link['Link']['created']);
		return $createdinterval<Configure::read('Monky.Votes.time_enabled');
	}
	
	/**
	 * 
	 * Cuenta los envios de un usuario
	 * @param $days
	 * @param $blogKey si es null cuenta todos los envios, si tiene la llave cuenta los de ese blog
	 * @param $user_id id del usuario, si es null usa el $data[Link][user_id]
	 */
	function getTotalSents($days=null,$blogKey=null,$user_id=null){
		if(!$user_id){
			$user_id=$this->data['Link']['user_id'];
		}
						
		$conditions=array('Link.user_id'=>$user_id);
		if($days) $conditions['Link.status_modified >']='date_sub(now(), interval '.$days.' day)';
						
		if($blogKey){		
			$conditions['blog_id']=$this->Blog->field('id',array('key'=>$blogKey));
		}
			
		return $this->find('count',array(
			'conditions'=>$conditions,
			'recursive'=>-1
		));
			
	}
    
	function getDuplicateLinkId($linkData){
		if(!isset($linkData['Link']['url'])&&!isset($linkData['Link']['checksum'])) return false;
		
		$conditions=array('OR'=>array());
		if(isset($linkData['Link']['url'])){
			$conditions['OR'][]=array('url'=>$linkData['Link']['url']);
			$conditions['OR'][]=array('url'=>str_replace('http://www.','http://',$linkData['Link']['url']));		
		}
		
		if(isset($linkData['Link']['checksum'])) $conditions['OR'][]=array('checksum'=>$linkData['Link']['checksum']);
		
		return $this->field('id',$conditions);		
	}
	
	function getPageData($url=null){
		if(!$this->pageObj) $this->setPageobj($url);
		
		return array(
			'link_title'=>$this->pageObj->title,
			'link_content'=>$this->pageObj->description,
			'link_images'=>$this->pageObj->images,
			'checksum'=>$this->pageObj->checksum,
		); 
	}
	
	function setUrlValidate(){
		$this->validate['url']=array(
	   		array(
	   			'rule'=>'notEmpty',
	   			'message'=>__('Enter a url',true),
	   			'last' => true
	   		),array(
				'rule' => array('url', true),
    			'message'=>__('The url is invalid',true),
    			'last' => true
    		),array(
    			'rule' => array('isFound'),
    			'last' => true
			),array(
    			'rule' => array('isContentShare'),
    			'message'=>__('The site owner of the site doesnt wanna share the content',true),
    			'last' => true
    		),array(
	    		'rule'=> array('notBannedHostname'),
    			'message'=>__('The site is banned',true),
    			'last' => true
    		),array(
    			'rule' => array('validateDuplicate'),
    			'last' => true
    		),array(
	    		'rule' => array('validateAutobombo'),
    			'message'=>__('You have send too many links',true),
    			'last' => true
    		)  		
    	);
		
	}
	
	function setValidateStep1(){
		$this->setUrlValidate();
	}
	
	function setValidateStep2(){
		
		$this->validate=array(
			'title'=>array(
				array(
    				'rule' =>array('notEmpty'),
    				'message'=>__('Write at least 15 characters',true),
				),
    			array(
    				'rule' =>array('minLength',15),
    				'message'=>__('Write at least 15 characters',true),
					'allowEmpty' => false
    			)
    		),
    		'content'=>array(
    			array(
    				'rule' =>array('minLength',30),
    				'message'=>__('Enter a longer description, write at least 30 characters',true),
    				'allowEmpty' => false
    			)
    		),
    		'category_id' => array(
        		'rule' => array('comparison', '>', 0),  
        		'message' => __('Please choose a category',true),
    			'allowEmpty' => false
    		)
    	);
    	
    	$this->setUrlValidate();
	}
	
	public function prepareDataForAddLink($data,$user_id){
		
		$data['Link']=am($data['Link'],array(
			'title'=>superTrim($data['Link']['title']),
			'content'=>superTrim($data['Link']['content']),
			'user_id'=>$user_id,
			'blog_id'=>$this->Blog->getBlogData($this->pageObj),
			'status_modified'=>getTimestamp(),
			'ip'=>Visitor::ip_int()
		));

		return $data;
	}
	
	function addLinkByUser($data,$user_id){
		$this->setValidateStep2();
				
		$data=$this->prepareDataForAddLink($data,$user_id);
		
		if(!$this->save($data,true,array('title','tags','content','category_id','url','blog_id','link_title','link_content','type','user_id','thumb','ip','checksum','status_modified'))) return false;
		
		if(isset($data['Link']['thumb'])){
			$data['Link']['thumb']=str_replace(array('..',DS), '', $data['Link']['thumb']);
			if (file_exists(WWW_ROOT.'img'.DS.'link'.DS.'tmp'.DS.$data['Link']['thumb'].'.jpg')){
				@copy( WWW_ROOT.'img'.DS.'link'.DS.'tmp'.DS.$data['Link']['thumb'].'.jpg', WWW_ROOT.'img'.DS.'link'.DS.$data['Link']['thumb'].'.jpg');
			}  
		}

		//add vote
		try{
			$this->vote(array(
				'Link'=>array('id'=>$this->id),
				'User'=>array(
					'id'=>$user_id,
					'vote'=>1,
					'karma'=>round($this->User->field('karma',array('id'=>$user_id)),2),
				)
			));
		} catch (ErrorVoteException $e){}
		
		
		//add pingback
		if($this->Blog->field('pingback',array('id'=>$data['Link']['blog_id']))){
			$this->Trackback->save(array(
				'link_id'=> $this->id,
				'trackback'=>$this->pageObj->getTrackback()
			));
		}
		
		$log = ClassRegistry::init('Log','model');
		$log->save(array('Log'=>array(
			'type' => 'link_new',
			'ref_id' => $this->id,
			'user_id' => $user_id,
			'ip' => 0
		)));
		
		return true;
	}
	
	private function _incrementVotes($vote){
		$vote=$this->prepareFields($vote, array('status'));
		
		$anonNegative=false;
		$toUpdate='anonymous';
		
		if ($vote['User']['id']>0 && $vote['User']['vote'] > 0) {
			$toUpdate='votes';
		}elseif ($vote['User']['id']>0 && $vote['User']['vote'] < 0) {
			$toUpdate='negatives';
		}elseif ($vote['User']['id']==0 && $vote['User']['vote'] < 0) {
			$toUpdate='anon_negatives';
			$anonNegative=true;
		}
		
		$this->updateAll(array($toUpdate=>$toUpdate.'+1'),array('id'=>$vote['Link']['id']),-1);
		
		if (!$anonNegative && $vote['Link']['status'] != 'published') {
			$addKarma=$this->getKarmaAddValue($vote);
			
			// If not published we update karma and count all votes
			$this->updateAll(array('karma'=>'Link.karma+'.$addKarma),array('id'=>$vote['Link']['id']),-1);
		}
				
	}
	
	/**
	 * 
	 * obtiene el valor del karma a sumar al link
	 */
	function getKarmaAddValue($vote){
		$karma_value = 0;
		
		$vote=$this->prepareFields($vote, array('status','user_id'));
				
		if ($vote['Link']['status'] != 'published') {
			$affinity = $this->User->getAffinity($vote['User']['id'],$vote['Link']['user_id']);
			if($vote['User']['vote'] < 0 && $vote['User']['id']>0) {
				if ($vote['User']['id'] != $vote['Link']['user_id'] && $affinity <  0 ) {
					$karma_value = round(min(-5, $vote['User']['karma'] *  $affinity/100),2);
				} else {
					$karma_value = round(-$vote['User']['karma'],2);
				}
			} else {
				if ($vote['User']['id']>0 && $vote['User']['id'] != $vote['Link']['user_id'] && $affinity > 0 ) {
					$karma_value = $value = round(max($vote['User']['karma'] * $affinity/100, 5));
				} else {
					$karma_value=round($vote['User']['vote'],2);
				}
			}
		} 
				
		return $karma_value;
	}	
	
	/*
	 * Usado para calcular la affinidad, usado por model User
	 */
	function getAffinity($iduser,$affinity=array(),$min_karma=200){
				
		$qry= $this->find('all',array(
			'fields'=>array('GROUP_CONCAT(Link.id) idlist'),
			'conditions'=>array( 
				'Link.user_id'=>$iduser, 
				'Link.created > date_sub(now(), interval 30 day)', 
				'Link.karma >'=>$min_karma
			),'recursive'=>-1
		));
		
		if(empty($qry)){
			return $affinity;
		}
		
		$ids=$qry[0][0]['idlist'];
		$nlinks = substr_count($ids, ',')+1;
		if ($nlinks <= 4) {
			return $affinity;
		}

		$votes = $this->Vote->find('all',array(
			'fields'=>array('Vote.user_id','sum(Vote.value/abs(Vote.value)) as `count`'),
			'conditions'=>array('Vote.link_id'=>explode(',', $ids),'Vote.type'=>'links'),
			'group'=>'Vote.user_id',
			'recursive'=>-1
		));
			
		foreach ($votes as $vote) {
			$user_id=$vote['Vote']['user_id'];
			$count=$vote['count'];
			if ($user_id > 0 && $user_id != $iduser && abs($count) > max(1, $nlinks/10) ) {
				$w = min(1, $nlinks/10);
				$w = max(0.7, $w);
				$c = $count/$nlinks * $w;
				if ($count > 0) {
					$affinity[$user_id] = round((1 - $c)*100);  // store as int (percent) to save space,
				} else {
					$affinity[$user_id] = round((-1 - $c)*100);  // store as int (percent) to save space,
				}
			}
		}
		
		return $affinity;
	
	}
	
	function countVoteClons($days=7,$user_id) {
		return $this->Vote->countClons('links', $this->id, $user_id,$days);
	}
	
	
	private function _getDataForRecalculateKarma($past_karma,$users_karma_avg){
		$votes_pos = $votes_neg = $karma_pos_user_high = $karma_pos_user_low = $karma_neg_user = 0;
		$affinity = $this->User->getAffinity($this->field('user_id'),$past_karma);
				
		$votes_pos_anon = $this->Vote->find('count',array(
			'conditions'=>array('Vote.type'=>'links','Vote.link_id'=>$this->id,'Vote.user_id' => 0,'Vote.value >'=> 0),
			'recursive'=>-1
		));
		
		$votes = $this->Vote->find('all',array(
			'fields'=>array('Vote.value'),
			'contains'=>array('User'=>array('id', 'karma')),
			'conditions'=>array('Vote.type'=>'links','Vote.link_id'=>$this->id,'Vote.user_id >'=>0)
		));
		
		// high =~ users with higher karma greater than average
		// low =~ users with higher karma less-equal than average
		$n = $vlow = $vhigh = 0;
		foreach ($votes as $vote) {
			extract($vote);
			if ($Vote['value'] > 0) {
				$votes_pos++;
				if ($affinity && $affinity[$User['id']] > 0) {
					$n++;
					// Change vote_value if there is affinity
					$Vote['value'] = max($User['karma'] * $affinity[$User['id']]/100, 6);
				} 
				if ($Vote['value'] >=  $users_karma_avg) {
					$karma_pos_user_high += $Vote['value'];
					$vhigh++;
				} else {
					$karma_pos_user_low += $Vote['value'];
					$vlow++;
				}
			} else {
				$votes_neg++;
				if ($affinity && $affinity[$User['id']] < 0) {
					$karma_neg_user += min(-6, $User['karma'] *  $affinity[$User['id']]/100);
				} else {
					$karma_neg_user -= $User['karma'];
				}
			}
		}
		if ($n > $votes_pos/5) {
			$perc = intval($n/$votes_pos * 100);
		}
		$qry = $this->Vote->find('all',array(
			'fields'=>array('sum(Vote.value) as `sumvalue`'),
			'conditions'=>array('Vote.type'=>'links','Vote.link_id'=>$this->id,'Vote.user_id'=> 0,'Vote.value >'=> 0),
			'recursive'=>-1
		));
		$karma_pos_ano=empty($qry)? 0:$qry[0][0]['sumvalue'];
		
		return compact('votes_pos','votes_neg','karma_pos_user_high','karma_pos_user_low','karma_neg_user'
		,'karma_pos_ano','perc','vlow','vhigh','votes_pos_anon');
		
	}
	
	
	function recalculateKarma($id=null,$users_karma_avg,$past_karma){
		if($id) $this->id=$id;
				
		if (! $users_karma_avg ) {
			$users_karma_avg=$this->User->getKarmaAvg(72);
		}
				
		extract($this->_getDataForRecalculateKarma($past_karma,$users_karma_avg));
			
		if ($this->field('votes') != $votes_pos || $this->field('anonymous') != $votes_pos_anon || $this->field('negatives') != $votes_neg) {
			$votes = $votes_pos;
			$anonymous = $votes_pos_anon;
			$negatives = $votes_neg;
			$this->save(array('Link'=>compact('votes','anonymous','negatives')),false,array('votes','anonymous','negatives'));
		}

		// Make sure we don't deviate too much from the average (it avoids vote spams and abuses)
		// Allowed difference up to 3%$karma_pos_user_high
		if ($karma_pos_user_low/$karma_pos_user_high > 1.15) {
			$perc = intval($vlow/($vlow+$vhigh) * 100);
			//$this->annotation .= $perc._('% de votos con karma menores que la media')." (".round($globals['users_karma_avg'],2).")<br/>";
		}
		$karma_pos_user = (int) $karma_pos_user_high + (int) min(max($karma_pos_user_high * 1.07, 4), $karma_pos_user_low);
		$karma_pos_ano = min($karma_pos_user_high*0.1, $karma_pos_ano);

		// Small quadratic punishment for links having too many negatives
		if ($karma_pos_user+$karma_pos_ano > abs($karma_neg_user) && abs($karma_neg_user)/$karma_pos_user > 0.075) {
			$r = min(max(0,abs($karma_neg_user)*2/$karma_pos_user), 0.5); 
			$karma_neg_user = max(-($karma_pos_user+$karma_pos_ano), $karma_neg_user * pow((1+$r), 2));
		}
	


		// BONUS
		// Give more karma to news voted very fast during the first two hours (ish)
		if (abs($karma_neg_user)/$karma_pos_user < 0.05 
			&& (time() - getUnixtime($this->field('created')) < 7200) 
			&& (time() - getUnixtime($this->field('created')) > 600)) { 
			$coef = Configure::read('Monky.Links.bonus_coef') - ($globals['now']-$this->sent_date)/7200;
			// It applies the same meta coefficient to the bonus'
			// Check 1 <= bonus <= $bonus_coef
			$coef = max(min($coef, Configure::read('Monky.Links.bonus_coef')), 1);
			// if it's has bonus and therefore time-related, use the base min_karma
		} elseif ($karma_pos_user+$karma_pos_ano > abs($karma_neg_user)) {
			// Aged karma
			$plain_hours = Configure::read('Monky.Links.karma_start_decay');
			$max_hours = Configure::read('Monky.Links.karma_decay');

			$d = 3600*$max_hours*(1+Configure::read('Monky.Links.min_decay'));
			$diff = max(0, time() - ($this->field('created') + $plain_hours*3600)); 
			$c = 1 - $diff/$d;
			$c = max(Configure::read('Monky.Links.min_decay'), $c);
			$c = min(1, $c);
			$coef = $c;
		} else {
			$coef = 1;
		}

		// Give the "new source" only if if has less than %5 of negative karma
		if (abs($karma_neg_user)/$karma_pos_user < 0.05) {
			$c = $this->calculateSourceBonus();
			if ($c > 1) {
				$coef = min(Configure::read('Monky.Links.bonus_coef'), $coef*$c);
				$c = round($c, 2);
			}
		}

		$karma = ($karma_pos_user+$karma_pos_ano+$karma_neg_user)*$coef;
		$category_coef=$this->Category->field('calculated_coef',array('id'=>$this->field('category_id')));
		if ($category_coef>0) {
			$karma *= $category_coef;
		}
						
		$this->save(array('Link'=>array('karma'=>round(round($karma,2)))),false,array('karma'));
		
		return compact('coef');
		
	}
	
	// Bonus for sources than are not frequently sent
	private function calculateSourceBonus() {
		$hours = $this->find('first',array(
			'fields'=>array('('.time().' - unix_timestamp(Link.created))/3600 as `hours`'),
			'conditions'=>array('Link.blog_id'=>$this->field('blog_id'), 'Link.id <'=>$this->id),
			'order'=>'Link.id desc'
		));
		$hours=empty($hours)? -1 : $hours['hours'];
		
		if (($hours>0) || $hours > Configure::read('Monky.Source.new_max_hours')) $hours = Configure::read('Monky.Source.new_max_hours');
		if ($hours >= 24) {
			return 1 + (Configure::read('Monky.Source.new_bonus') - 1) * ($hours - Configure::read('Monky.Source.new_min_hours')) / (Configure::read('Monky.Source.new_max_hours') - Configure::read('Monky.Source.new_min_hours'));
		} 
		return 0;
	}
	
	private function addLinkVote($vote){
		$tb=$this->Vote->tablePrefix.$this->Vote->table;
		
		//dont add negatives anon votes
		if(!($vote['User']['id']==0 && $vote['User']['vote']<0)) $vote['User']['vote']==0;
				
		$addQry=$this->query("INSERT HIGH_PRIORITY INTO $tb (`type`, `user_id`, `link_id`, `value`, `ip`, `created`) VALUES 
		  ('links', ".$vote['User']['id'].", ".$vote['Link']['id'].",	".$vote['User']['vote'].", ".Visitor::ip_int().", CURRENT_TIMESTAMP )");
		
		if(!$addQry){
			throw new ErrorVoteException(null,__('couldnt add vote',true));
		} 
				
		$this->_incrementVotes($vote);
	}
	
    /**
     * 
     * chequea que este seteado el id, que no este baneada la ip,
     * que sea votable, que se permitan votos anonimos, 
     * que si no fue publicada no supere ceirta cantidad de votos anonimos
     * que no se vote usando clons y la frecuencia de votos
     */
	private function _checkErrorInVote($vote){
		
		try{
			$this->_checkLinkExist($vote);
			$this->User->checkIpNotBanned();
			$this->_checkLinkIsVotable($vote);
			$this->_checkErrorInAnonVote($vote);
			$this->_checkUserHasVote($vote);
			$this->_checkLinkVoteClons($vote);
			$this->_checkVoteFreq($vote);

			} catch (UserErrorException $exception){
			throw new ErrorVoteException($exception->getMessage());
		} catch (LinkErrorException $exception){
			throw new ErrorVoteException($exception->getMessage());
		}
		
	}
	
	private function _checkUserHasVote($vote){
		if($this->Vote->userHasVoteLink($vote['Link']['id'],$vote['User']['id'])){
			if($vote['User']['id']>0){
				throw new ErrorVoteException(__('you have already vote with this IP or username',true));
			}else{
				throw new ErrorVoteException(__('the IP have already vote this link',true));		
			}
		}
	}
	
	private function _checkLinkExist($link){
		if(!$this->hasAny(array('id'=>$link['Link']['id']))){
			throw new LinkErrorException(__('Link doesnt exist',true));
		}
	}
		
	private function _checkLinkIsVotable($link){
		if(!$this->isVotable($link)){
			throw new ErrorVoteException(__('votes closed',true));
		}
	}
	
	private function _checkLinkVoteClons($vote){
		if($this->Vote->countLinkVoteClons($vote['Link']['id'],$vote['User']['id'],5)>2){
			throw new ErrorVoteException(null,__('you can not vote with clons',true));
		}
	}
		
	private function _checkVoteFreq($vote){
		$vote=$this->prepareFields($vote, array('status'));
		
		$votes_freq = $this->Vote->getVoteFreq($vote);

		$freq=($vote['User']['id']>0)? 3 : 2;

		// Allow to play a little more if published
		if ($vote['Link']['status'] == 'published')  $freq *= 2; 
				
		if ($votes_freq > $freq) {
			if ($vote['User']['id']>0 && $vote['User']['karma'] > 4 && $vote['Link']['status'] != 'published') {
		    	// Crazy votes attack, decrease karma
				// she does not deserve it :-)
				
    			$this->User->updateAll(array('karma'=>'User.karma - '.Configure::read('Monky.Votes.abuse_karma_decrease')),array('id'=>$vote['User']['id']));
    			
				throw new ErrorVoteException(null,__('easy cowboy!',true). ', ' . sprintf(__('your karma has decreased %s points',true),Configure::read('Monky.Votes.abuse_karma_decrease')));
			}
			throw new ErrorVoteException(null, __('easy cowboy!',true));
		}
				
	}
	
	private function _checkErrorInAnonVote($vote){
		if ($vote['User']['id']) return;
		
		if(!Configure::read('Monky.Votes.anon_enable')) {
			throw new ErrorVoteException(null,__('Anon votes are disabled',true));
		}
		
		$vote=$this->prepareFields($vote, array('anonymous','votes','status'));
				
		// Check that there are not too much annonymous votes
		$anon_to_user_votes=Configure::read('Monky.Votes.anon_to_user');
		// Allow more anon votes if published.
		if ($vote['Link']['status'] == 'published') $anon_to_user_votes = max(3, $anon_to_user_votes); 
		
		if ($vote['Link']['anonymous'] >  ($vote['Link']['votes'] * $anon_to_user_votes)) {
			throw new ErrorVoteException(__('Too many anon votes in this link, register to vote or try later',true));
		}		
	}

}
